package com.controller.sys;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.common.base.BaseConstant;
import com.common.util.EncryptionUtil;
import com.common.util.RedisUtil;
import com.common.util.ResultUtil;
import com.common.vo.ExceptionVo;
import com.common.vo.ResultVo;
import com.entity.sys.SysUser;
import com.service.sys.SysUserService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;


@RestController
@RequestMapping("/sys")
public class SysLoginController {

    @Resource
    private SysUserService sysUserService;
    @Resource
    private RedisUtil redisUtil;

    /** 密码最大错误次数 */
    private int ERROR_COUNT = 3;

    /** pc端登录 */
    @PostMapping("/pc/login")
    public ResultVo pcLogin(String userName, String password, String code, HttpServletRequest request){
        try {
            Object verCode = redisUtil.get(BaseConstant.verCode+code);
            if (null == verCode) {
                return ResultUtil.error("验证码已失效，请重新输入");
            }
            String verCodeStr = verCode.toString();
            if (verCodeStr == null || StrUtil.isEmpty(code) || !verCodeStr.equalsIgnoreCase(code)) {
                return ResultUtil.error("验证码错误");
            }else if (!redisUtil.hasKey(BaseConstant.verCode+code)) {
                return ResultUtil.error("验证码已过期,请重新输入");
            }else {
                redisUtil.del(BaseConstant.verCode+code);
            }
            SysUser user = passwordErrorNum(userName, password);
            StpUtil.login(user.getId(),"PC");
            String tokenValue = StpUtil.getTokenValue();
            StpUtil.getSession().set("user",user);
            return ResultUtil.success(tokenValue);
        } catch (ExceptionVo e) {
            return ResultUtil.error(e.getCode(),e.getMessage());
        }catch (Exception e) {
            e.printStackTrace();
            return ResultUtil.error(BaseConstant.UNKNOWN_EXCEPTION);
        }
    }

    /*@PostMapping("/app/login")
    public ResultVo appLogin(String userName, String password){
        try {
            //userName = RSAUtil.decrypt(userName,"");
            //password = RSAUtil.decrypt(password,"");
            String s = SecureUtil.sha256(password);
            SysUser user = passwordErrorNum(userName, s);
            StpUtil.login(user.getId(),"APP");
            String tokenValue = StpUtil.getTokenValue();
            SaSession session = StpUtil.getTokenSession();
            session.set("user",user);
            return ResultUtil.success(tokenValue);
        } catch (ExceptionVo e) {
            return ResultUtil.error(e.getCode(),e.getMessage());
        }catch (Exception e) {
            e.printStackTrace();
            return ResultUtil.error("未知异常");
        }
    }*/

    @PostMapping("/test")
    public ResultVo test(){
        Map<String,Object> map = new HashMap<>();
        SaSession session = StpUtil.getSession();
        map.put("user",session.get("user"));
        map.put("permission",StpUtil.getPermissionList());
        map.put("tokenInfo",StpUtil.getTokenInfo());
        return ResultUtil.success(map);
    }

    /** 获取验证码 */
    @GetMapping(value = "/getCode")
    public void getCode(HttpServletResponse response) {
        try {
            response.setHeader("Pragma", "No-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setContentType("image/jpeg");

            RandomGenerator randomGenerator = new RandomGenerator(BaseConstant.captcha, 4);
            LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(100, 42,4,50);
            lineCaptcha.setGenerator(randomGenerator);
            BufferedImage image = lineCaptcha.getImage();
            OutputStream out = response.getOutputStream();
            redisUtil.set(BaseConstant.verCode+lineCaptcha.getCode(), lineCaptcha.getCode(), 60);
            ImageIO.write(image, "png", out);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** 退出登录 */
    @DeleteMapping("/logout")
    public ResultVo logout(String id,String loginType){
        StpUtil.logout(id,loginType);
        return ResultUtil.success("退出登录成功");
    }

    /**
     * 判断账号是否锁定
     */
    private boolean lockedUser(long currentTime,String userName){
        boolean flag = false;
        if (redisUtil.hasKey(BaseConstant.ERROR_COUNT+userName)){
            long loginTime = Long.parseLong(redisUtil.hget(BaseConstant.ERROR_COUNT+userName, "loginTime"));
            int i = Integer.parseInt(redisUtil.hget(BaseConstant.ERROR_COUNT+userName,"errorNum").toString());
            if (i >= ERROR_COUNT && currentTime < loginTime){
                Duration between = LocalDateTimeUtil.between(LocalDateTimeUtil.of(currentTime), LocalDateTimeUtil.of(loginTime));
                throw new ExceptionVo(1004,"账号锁定中，还没到允许登录的时间，请"+between.toMinutes()+"分钟后再尝试");
            }else{
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 密码错误次数验证
     */
    private SysUser passwordErrorNum(String userName,String password) throws InvalidKeySpecException, NoSuchAlgorithmException {
        //查询用户
        SysUser user = sysUserService.getUserByName(userName);
        if (null == user){
            throw new ExceptionVo(1001,"用户不存在");
        }
        //根据前端输入的密码（明文），和加密的密码、盐值进行比较，判断输入的密码是否正确
        boolean authenticate = EncryptionUtil.authenticate(password, user.getPassword(), user.getSalt());
        if (authenticate) {
            //密码正确错误次数清零
            redisUtil.del(BaseConstant.ERROR_COUNT+userName);
        } else {
            long currentTime = System.currentTimeMillis();
            //判断账号是否锁定
            boolean flag = lockedUser(currentTime, userName);
            //错误3次，锁定15分钟后才可登陆 允许时间加上定义的登陆时间（毫秒）
            long timeStamp = System.currentTimeMillis()+900000;
            //密码登录限制（0：连续错3次，锁定账号15分钟。1：连续错5次，锁定账号30分钟）
            if (redisUtil.hasKey(BaseConstant.ERROR_COUNT+userName)){
                int i = Integer.parseInt(redisUtil.hget(BaseConstant.ERROR_COUNT+userName,"errorNum"));
                if (flag && i==ERROR_COUNT){
                    redisUtil.hset(BaseConstant.ERROR_COUNT+userName,"errorNum",1);
                }else {
                    redisUtil.hincr(BaseConstant.ERROR_COUNT+userName,"errorNum",1);
                }
                redisUtil.hset(BaseConstant.ERROR_COUNT+userName,"loginTime",timeStamp);
            }else {
                Map<String,Object> map = new HashMap<>();
                map.put("errorNum",1);
                map.put("loginTime",timeStamp);
                redisUtil.hmset(BaseConstant.ERROR_COUNT+userName, map, -1);
            }
            int i = Integer.parseInt(redisUtil.hget(BaseConstant.ERROR_COUNT+userName,"errorNum"));
            if (i==ERROR_COUNT){
                throw new ExceptionVo(1004,"您的密码已错误"+ERROR_COUNT+"次，现已被锁定，请15分钟后再尝试");
            }
            throw new ExceptionVo(1000,"密码错误，总登录次数"+ERROR_COUNT+"次，剩余次数: " + (ERROR_COUNT-i));
        }
        return user;
    }

}
